#include "bpt_acsvr.h"
#include "z_base_api.h"
#include "z_server.h"
#include "gsmeg_link_manager.h"

const char * __OS_VERSION__ = SVR_NAME;
bpt_acsvr_ctx_t *g_ctx = NULL;

int isnot_acw_log_body = 0;

static void __dev_st_ntf(int grp, int dev, int st) {
    printf("__dev_st_ntf grp[%d], dev[%d], st[%d]\n", grp, dev, st);
}

static int __out_channel = 0;

static int __acr2p_cb(void *gtp, int channel, int port) {
    if(gtp){
        LOG_GTP_FULL(DEBUG, "__acr2p_cb:", (z_gtp_t *)gtp);
        if (channel != __out_channel) {
            Z_LOG_NOTICE("channel changed: __out_channel = %d, channel = %d", __out_channel, channel);
            __out_channel = channel;
        }
        return handle_gtp_input((z_gtp_t *)gtp,channel,port);

    }else{
        ERROR_PRINT(" msg in acr2p_cb is NULL");
        return -1;
    }
}

static int __m2p_cb(void *task) {
    if(task){
        int channel = z_api_lrm_get_channel_from_recv_msg(task);//todo --any exception
        INFO_PRINT("recv gsmeg from channel[%l]",channel);
        LOG_GSMEG_HEAD(DEBUG, "__m2p_cb channel", ((z_gsmeg_t *)task));
        return handle_gsmeg_input((z_gsmeg_t *)task,channel);
    }else{
        ERROR_PRINT(" msg in m2p_cb is NULL");
        return -1;
    }
}

static void __channel_err_handler(int channel){
    send_logout_req_and_clean_info(channel);
 }

static int __out_channel_error(int channel, int port) {
	Z_LOG_NOTICE("channel[%d] error", channel);
    __channel_err_handler(channel);
	return 0;
}

static ac_flow_ctrl_t *__init_flow_ctrl(z_hash_t *cfg){
    ac_flow_ctrl_t *flow_ctrl =init_flow_ctrl();
    _CHECK_VAL_TRUE_RET(flow_ctrl == NULL,NULL,"flow ctrl calloc init failure") 

    z_string_t *fname = zH_strfind_string(cfg,"flow_ctrl");
    _CHECK_VAL_TRUE_RET(fname == NULL,NULL,"flow ctrl file not found") 
    _CHECK_VAL_TRUE_RET(load_flow_ctrl(string2str(fname),flow_ctrl) != 0,NULL,"load_flow_ctrl failure") 

    set_flow_ctrl_switch(flow_ctrl->is_flow_ctrl);
    if(flow_ctrl->is_flow_ctrl){
        set_default_flow_ctrl(flow_ctrl->default_flow_ctrl);
    }
    return flow_ctrl;
}

static int __acsvr_ctx_init(z_hash_t *cfg){
    g_ctx = (bpt_acsvr_ctx_t *)calloc(1,sizeof(bpt_acsvr_ctx_t)); 

    g_ctx->link_id_map_=zH_init();
    g_ctx->user_id_map_=zH_init();
    g_ctx->trader_id_map_=zH_init();
    g_ctx->seat_id_map_=zH_init();
    g_ctx->version_cfg_=zH_init();
    g_ctx->gtp_cvt_handle_ = cvt_init();

    g_ctx->gsmeg_link_hashmap_ = gsmeg_link_map_create();
    _CHECK_VAL_TRUE_RET(NULL == g_ctx->gsmeg_link_hashmap_,-1,"gsmeg link map create fail")

    z_hash_t *gsmeg_links_cfg = (z_hash_t *)zH_strfind(cfg,"gsmeg_links");
    _CHECK_VAL_TRUE_RET(NULL == gsmeg_links_cfg,-1,"gsmeg link is not configed")
    g_ctx->gsmeg_link_cfg_ = gsmeg_links_cfg;
    
    g_ctx->ready_flag_ = FALSE;
    
    ALLOC_GTP(g_ctx->gtp_pub_,GTP_CONT_LEN);
    
    _CHECK_VAL_TRUE_RET( NULL ==(g_ctx->cvt_handle_ =convert_handle_create(cfg)),-1,"create convert handle failure")


    _CHECK_VAL_TRUE_RET(NULL == zH_strfind_ll(cfg, "user_limit",(long long *)&(g_ctx->user_limit_)),-1,"user limit is not configed")

    INFO_PRINT("init version cfg ....");
    z_string_t *tmp = zH_strfind_string(cfg,"version_cfg");
    _CHECK_VAL_TRUE_RET(NULL == tmp,-1,"version cfg is not configed")
    g_ctx->version_cfg_ = z_json_file(string2str(tmp));
    _CHECK_VAL_TRUE_RET(NULL == g_ctx->version_cfg_,-1,"version cfg format error")

    _CHECK_VAL_TRUE_RET(NULL == zH_strfind_ll(cfg,"dev_id",(long long *)&(g_ctx->dev_id_)),-1,"dev_id is not configed")    

    tmp = zH_strfind_string(cfg,"dev_name");
    _CHECK_VAL_TRUE_RET(NULL == tmp,-1,"dev name is not configed")
    strlcpy(g_ctx->dev_name_,string2str(tmp),sizeof(g_ctx->dev_name_));

    INFO_PRINT("init guomi safe managers ....");
    _CHECK_VAL_TRUE_RET(0 !=create_guomi_trader_safemap(&(g_ctx->enc_mode_manager_),&(g_ctx->enc_pub_key_manager_),&(g_ctx->sign_pub_key_manager_)),-1,"guomi safe managers init fail")
    INFO_PRINT("init guomi trader safe info ....");
    _CHECK_VAL_TRUE_RET(0 != init_guomi_trader_safeinfo_by_cfg(cfg,g_ctx->enc_mode_manager_,g_ctx->enc_pub_key_manager_,g_ctx->sign_pub_key_manager_),-1,"trader guomi info init failed") 

    INFO_PRINT("init guomi safe api ....");
    _CHECK_VAL_TRUE_RET(0 !=init_guomi_safe_by_cfg(cfg,&(g_ctx->safe_server_handle_),&(g_ctx->guomi_safe_ctx_),g_ctx->enc_mode_manager_,g_ctx->enc_pub_key_manager_,g_ctx->sign_pub_key_manager_) ,-1,"guomi safe api init fail")
    INFO_PRINT("init guomi safe link ....");
    _CHECK_VAL_TRUE_RET(0 !=guomi_safe_create_link(g_ctx->safe_server_handle_,&(g_ctx->safe_server_link_)) ,-1,"guomi safe link init fail")
    
    INFO_PRINT("init flow control ....");
	_CHECK_VAL_TRUE_RET( NULL == (g_ctx->flow_ctrl_ =__init_flow_ctrl(cfg)),-1,"init flow ctrl failure")

    INFO_PRINT("init right cfg ....");          
    tmp = zH_strfind_string(cfg,"right_cfg");
    _CHECK_VAL_TRUE_RET(NULL == tmp,-1,"right cfg is not configed")
    z_hash_t *right_cfg = z_json_file(string2str(tmp));
    _CHECK_VAL_TRUE_RET(NULL == right_cfg,-1,"right cfg format error")  
    g_ctx->trader_right_ = (z_hash_t *)zH_strfind(right_cfg,"trader");

    INFO_PRINT("init flow seq_no manager ...");
    tmp = zH_strfind_string(cfg,"flow_seq_no_file");
    _CHECK_VAL_TRUE_RET(NULL == tmp,-1,"flow_seq_no file is not configed")

    g_ctx->flow_seq_manager_ = flow_seq_manager_create();
    _CHECK_VAL_TRUE_RET(NULL == g_ctx->flow_seq_manager_,-1,"flow_seq_manager create error")  
    flow_seq_manager_add(g_ctx->flow_seq_manager_,MARKET_FLOW_TYPE,string2str(tmp));

    INFO_PRINT("init gsmeg print blacklist cfg ...."); 
    tmp = zH_strfind_string(cfg,"gsmeg_print_blacklist_cfg");
    if(NULL == tmp){
        g_ctx->gsmeg_print_blacklist_ = NULL;
        WARNING_PRINT("gsmeg_print_blacklist cfg is not configed");
    }else{
        g_ctx->gsmeg_print_blacklist_ = z_json_file(string2str(tmp));
        _CHECK_VAL_TRUE_RET(NULL == g_ctx->gsmeg_print_blacklist_,-1,"gsmeg_print_blacklist  config json format error") 
    }

    return 0;
}

static int gsmeg_link_hash_connect(z_hash_t *gsmeg_link_cfg){
     z_pair_t *p = NULL;
     z_hash_t *gsmeg_link = NULL;
     z_string_t *ip = NULL;
     long long port=0,recon_interval=0,recv_timeout=0,hb_interval=0;
     while(NULL != (p = zH_foreach(gsmeg_link_cfg,p))){
         gsmeg_link = (z_hash_t *)(p->value_);
         ip = zH_strfind_string(gsmeg_link,"ip");
         _CHECK_VAL_TRUE_RET(NULL == ip,-1,"gsmeg link ip is not configed")
         _CHECK_VAL_TRUE_RET(NULL == zH_strfind_ll(gsmeg_link, "port", &port),-1,"gsmeg link port is not configed")
         _CHECK_VAL_TRUE_RET(NULL == zH_strfind_ll(gsmeg_link, "reconnect_interval", &recon_interval),-1,"gsmeg link port is not configed")
         _CHECK_VAL_TRUE_RET(NULL == zH_strfind_ll(gsmeg_link, "recv_timeout", &recv_timeout),-1,"gsmeg link recv timeout is not configed")
         _CHECK_VAL_TRUE_RET(NULL == zH_strfind_ll(gsmeg_link, "heartbeat_interval", &hb_interval),-1,"gsmeg link heartbeat interval is not configed")
         g_api_lrm->connect_(string2str(ip),port,recon_interval,recv_timeout,hb_interval); 
    }
    return 0 ;
}

#define GSMEG_REPAIR_INTERVAL 2000
static void gsmeg_repair_cb(void *ctx){
    long long seq_no = (long long)flow_seq_manager_get_seq_no(g_ctx->flow_seq_manager_,MARKET_FLOW_TYPE); 
    send_gsmeg_repair_req(g_ctx->gsmeg_link_hashmap_,MARKET_FLOW_TYPE,seq_no);

    flow_seq_manager_save(g_ctx->flow_seq_manager_,MARKET_FLOW_TYPE);
}


static int __acsvr_init(z_hash_t *cfg) {
    _CHECK_VAL_TRUE_RET(0 != __acsvr_ctx_init(cfg),-1,"acsvr ctx init failure")
    _CHECK_VAL_TRUE_RET(0 != gsmeg_link_hash_connect(g_ctx->gsmeg_link_cfg_),-1,"acsvr ctx init failure")
    z_register_timer_ms(gsmeg_repair_cb, GSMEG_REPAIR_INTERVAL,NULL, -1);
    return 0;
}

static void __in_channel_state_change(const char * ip, int port, int channel, int state) {
    Z_LOG_NOTICE("%s[%d], channel = %d, state = %d [1-registered 2-connected 3-timeout 4-tobeclosed 5-normalclosed 6-errclosed]", ip, port, channel, state);
    switch(state){
        case CHANNEL_STATE_REGISTERED:
            return;
        case CHANNEL_STATE_CONNECTED:
            Z_LOG_MONITOR("%s|%d|%s|%d",DEV_LOGIN_EVENT,g_ctx->dev_id_,ip,port);
            cb_on_gsmeg_link_connected(g_ctx->gsmeg_link_hashmap_,channel,g_ctx->dev_name_,g_ctx->dev_id_);  
            return;
        default:   
            Z_LOG_MONITOR("%s|%d|%s|%d",DEV_LOGOUT_EVENT,g_ctx->dev_id_,ip,port);
            cb_on_gsmeg_link_disconnected(g_ctx->gsmeg_link_hashmap_,channel);
            return;
    }
}


static void __print_cmd(const z_head_t *h, z_buffer_t *b) {
    if (h->cmd_ >= CMD_GUOMI_MIN && h->cmd_ <= CMD_GUOMI_MAX) print_cmd_guomi(h, b);
    else Z_LOG_ERROR("__print_cmd failed");
}

static z_server_t *z_bpt_ac_server_init(int argc, char **argv, dev_st_ntf_t _dev_st_ntf) {
    g_persudo = zH_init();
    z_persudo(g_guomi_name2cmd_mapping);
    z_server_t *svr = z_server_init(argc, argv, _dev_st_ntf);
    if (svr == NULL) return NULL;
    svr->print_cmd_ = __print_cmd;
    return svr;
}

int main(int argc, char **argv) {
    if (NULL == z_bpt_ac_server_init(argc, argv, __dev_st_ntf)) goto end;
    if (NULL == z_api_lrm_init(__in_channel_state_change, (handle_msg_t)__m2p_cb)) goto end;
    if (0 != ac_init(__acr2p_cb, __m2p_cb, __out_channel_error)) goto end;
	z_server_set_process_init(__acsvr_init);
    if (0 == z_server_run() && 0 == ac_run()) {
        ac_join();
        z_server_join();
    } 
end:
	// mem lose, but it's ok
	if (NULL != g_svr) z_server_free();
    return 0;
}

